home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / security / logdaemon-2 / rshd / rshd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-18  |  10.6 KB  |  480 lines

  1. /*
  2.  * Copyright (c) 1983, 1988 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that the above copyright notice and this paragraph are
  7.  * duplicated in all such forms and that any documentation,
  8.  * advertising materials, and other materials related to such
  9.  * distribution and use acknowledge that the software was developed
  10.  * by the University of California, Berkeley.  The name of the
  11.  * University may not be used to endorse or promote products derived
  12.  * from this software without specific prior written permission.
  13.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  14.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  15.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  16.  */
  17.  
  18. #ifndef lint
  19. char copyright[] =
  20. "@(#) Copyright (c) 1983, 1988 The Regents of the University of California.\n\
  21.  All rights reserved.\n";
  22. #endif /* not lint */
  23.  
  24. #ifndef lint
  25. static char sccsid[] = "@(#)rshd.c    5.17.1.2 (Berkeley) 2/7/89";
  26. #endif /* not lint */
  27.  
  28. /*
  29.  * remote shell server:
  30.  *    [port]\0
  31.  *    remuser\0
  32.  *    locuser\0
  33.  *    command\0
  34.  *    data
  35.  */
  36. #ifdef __svr4__
  37. #include <unistd.h>    /* for F_OK */
  38. /* XXX the next two should be keyed with their own #ifdef */
  39. #define    killpg(p,s)    kill(-(p),(s))
  40. #define    setpgrp(x,y)    setsid()
  41. #define    BSD_COMP    /* for sys/ioctl.h */
  42. #endif
  43.  
  44. #ifdef USE_STRING_H
  45. #define    index        strchr
  46. #define    rindex        strrchr
  47. #define    bcmp        memcmp
  48. #else
  49. #include <strings.h>
  50. #endif
  51.  
  52. #include <sys/param.h>
  53. #include <sys/ioctl.h>
  54. #include <sys/socket.h>
  55. #include <sys/file.h>
  56. #include <sys/time.h>
  57.  
  58. #include <netinet/in.h>
  59.  
  60. #include <arpa/inet.h>
  61.  
  62. #include <stdio.h>
  63. #include <errno.h>
  64. #include <pwd.h>
  65. #include <signal.h>
  66. #include <netdb.h>
  67. #include <syslog.h>
  68.  
  69. int    errno;
  70. int    keepalive = 1;
  71. char    *index(), *rindex(), *strncat();
  72. /*VARARGS1*/
  73. int    error();
  74.  
  75. #if defined(sun) || defined(ultrix)
  76. int _check_rhosts_file;
  77. #endif
  78.  
  79. /* Ultrix syslog(3) has no facility or options */
  80. #ifndef LOG_DAEMON
  81. #define LOG_DAEMON    0
  82. #define LOG_ODELAY    0
  83. #endif
  84.  
  85. char *procname;
  86.  
  87. /*ARGSUSED*/
  88. main(argc, argv)
  89.     int argc;
  90.     char **argv;
  91. {
  92.     extern int opterr, optind, _check_rhosts_file;
  93.     struct linger linger;
  94.     int ch, on = 1, fromlen;
  95.     struct sockaddr_in from;
  96.  
  97.     umask(022);
  98.  
  99.     procname = argv[0];
  100.  
  101.     openlog(procname, LOG_PID | LOG_ODELAY, LOG_DAEMON);
  102.  
  103.     opterr = 0;
  104.     while ((ch = getopt(argc, argv, "ln")) != EOF)
  105.         switch((char)ch) {
  106.         case 'l':
  107.             _check_rhosts_file = 0;
  108.             break;
  109.         case 'n':
  110.             keepalive = 0;
  111.             break;
  112.         case '?':
  113.         default:
  114.             syslog(LOG_ERR, "usage: rshd [-l]");
  115.             break;
  116.         }
  117.  
  118.     argc -= optind;
  119.     argv += optind;
  120.  
  121.  
  122.     fromlen = sizeof (from);
  123.     if (getpeername(0, &from, &fromlen) < 0) {
  124.         fprintf(stderr, "%s: ", argv[0]);
  125.         perror("getpeername");
  126.         _exit(1);
  127.     }
  128.     if (keepalive &&
  129.         setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
  130.         sizeof(on)) < 0)
  131.         syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
  132.     linger.l_onoff = 1;
  133.     linger.l_linger = 60;            /* XXX */
  134.     if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger,
  135.         sizeof (linger)) < 0)
  136.         syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
  137.     doit(&from);
  138. }
  139.  
  140. char    username[20] = "USER=";
  141. char    homedir[64] = "HOME=";
  142. char    shell[64] = "SHELL=";
  143. char    *envinit[] =
  144. #ifdef __svr4__
  145.         {homedir, shell, "PATH=/usr/bin:", username, 0};
  146. #else
  147.         {homedir, shell, "PATH=/usr/ucb:/bin:/usr/bin:", username, 0};
  148. #endif
  149. char    **environ;
  150.  
  151. doit(fromp)
  152.     struct sockaddr_in *fromp;
  153. {
  154.     char cmdbuf[NCARGS+1], *cp;
  155.     char locuser[16], remuser[16];
  156.     struct passwd *pwd;
  157.     int s;
  158.     struct hostent *hp;
  159.     char *hostname;
  160.     short port;
  161.     int pv[2], pid, cc;
  162.     int nfd;
  163.     fd_set ready, readfrom;
  164.     char buf[BUFSIZ], sig;
  165.     int one = 1;
  166.     char remotehost[2 * MAXHOSTNAMELEN + 1];
  167.  
  168.     (void) signal(SIGINT, SIG_DFL);
  169.     (void) signal(SIGQUIT, SIG_DFL);
  170.     (void) signal(SIGTERM, SIG_DFL);
  171. #ifdef DEBUG
  172.     { int t = open("/dev/tty", 2);
  173.       if (t >= 0) {
  174.         ioctl(t, TIOCNOTTY, (char *)0);
  175.         (void) close(t);
  176.       }
  177.     }
  178. #endif
  179.     fromp->sin_port = ntohs((u_short)fromp->sin_port);
  180.     if (fromp->sin_family != AF_INET) {
  181.         syslog(LOG_ERR, "malformed from address\n");
  182.         exit(1);
  183.     }
  184. #ifdef IP_OPTIONS
  185.       {
  186.     u_char optbuf[BUFSIZ/3], *cp;
  187.     char lbuf[BUFSIZ], *lp;
  188.     int optsize = sizeof(optbuf), ipproto;
  189.     struct protoent *ip;
  190.  
  191.     if ((ip = getprotobyname("ip")) != NULL)
  192.         ipproto = ip->p_proto;
  193.     else
  194.         ipproto = IPPROTO_IP;
  195.     if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) == 0 &&
  196.         optsize != 0) {
  197.         lp = lbuf;
  198.         for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
  199.             sprintf(lp, " %2.2x", *cp);
  200.         syslog(LOG_NOTICE,
  201.             "Connection received using IP options (ignored):%s", lbuf);
  202.         if (setsockopt(0, ipproto, IP_OPTIONS,
  203.             (char *)NULL, optsize) != 0) {
  204.             syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
  205.             exit(1);
  206.         }
  207.     }
  208.       }
  209. #endif
  210.  
  211.     if (fromp->sin_port >= IPPORT_RESERVED ||
  212.         fromp->sin_port < IPPORT_RESERVED/2) {
  213.         syslog(LOG_NOTICE, "Connection from %s on illegal port",
  214.             inet_ntoa(fromp->sin_addr));
  215.         exit(1);
  216.     }
  217.  
  218.     (void) alarm(60);
  219.     port = 0;
  220.     for (;;) {
  221.         char c;
  222.         if ((cc = read(0, &c, 1)) != 1) {
  223.             if (cc < 0)
  224.                 syslog(LOG_NOTICE, "read: %m");
  225.             shutdown(0, 1+1);
  226.             exit(1);
  227.         }
  228.         if (c == 0)
  229.             break;
  230.         port = port * 10 + c - '0';
  231.     }
  232.  
  233.     (void) alarm(0);
  234.     if (port != 0) {
  235.         int lport = IPPORT_RESERVED - 1;
  236.         s = rresvport(&lport);
  237.         if (s < 0) {
  238.             syslog(LOG_ERR, "can't get stderr port: %m");
  239.             exit(1);
  240.         }
  241.         if (port >= IPPORT_RESERVED) {
  242.             syslog(LOG_ERR, "2nd port not reserved\n");
  243.             exit(1);
  244.         }
  245.         fromp->sin_port = htons((u_short)port);
  246.         if (connect(s, fromp, sizeof (*fromp)) < 0) {
  247.             syslog(LOG_INFO, "connect second port: %m");
  248.             exit(1);
  249.         }
  250.     }
  251.  
  252. #ifdef ultrix /* inetd does not set up 1 and 2 */
  253.     dup2(0, 1);
  254.     dup2(0, 2);
  255. #endif
  256.  
  257. #ifdef notdef
  258.     /* from inetd, socket is already on 0, 1, 2 */
  259.     dup2(f, 0);
  260.     dup2(f, 1);
  261.     dup2(f, 2);
  262. #endif
  263.     hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr),
  264.         fromp->sin_family);
  265.     if (hp) {
  266.         /*
  267.          * If name returned by gethostbyaddr is in our domain,
  268.          * attempt to verify that we haven't been fooled by someone
  269.          * in a remote net; look up the name and check that this
  270.          * address corresponds to the name.
  271.          */
  272. #ifdef bug
  273.         if (local_domain(hp->h_name)) {
  274. #endif
  275.             strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1);
  276.             remotehost[sizeof(remotehost) - 1] = 0;
  277.             hp = gethostbyname(remotehost);
  278.             if (hp == NULL) {
  279.                 syslog(LOG_INFO,
  280.                     "Couldn't look up address for %s",
  281.                     remotehost);
  282.                 error("Couldn't look up address for your host");
  283.                 exit(1);
  284.             } else for (; ; hp->h_addr_list++) {
  285.                 if (!bcmp(hp->h_addr_list[0],
  286.                     (caddr_t)&fromp->sin_addr,
  287.                     sizeof(fromp->sin_addr)))
  288.                     break;
  289.                 if (hp->h_addr_list[0] == NULL) {
  290.                     syslog(LOG_NOTICE,
  291.                       "Host addr %s not listed for host %s",
  292.                         inet_ntoa(fromp->sin_addr),
  293.                         hp->h_name);
  294.                     error("Host address mismatch");
  295.                     exit(1);
  296.                 }
  297.             }
  298. #ifdef bug
  299.         }
  300.         hostname = hp->h_name;
  301. #else
  302.         hostname = remotehost;
  303. #endif
  304.     } else
  305.         hostname = inet_ntoa(fromp->sin_addr);
  306.  
  307.     getstr(remuser, sizeof(remuser), "remuser");
  308.     getstr(locuser, sizeof(locuser), "locuser");
  309.     getstr(cmdbuf, sizeof(cmdbuf), "command");
  310.     do_access(procname, hostname, inet_ntoa(fromp->sin_addr), 
  311.         remuser, locuser);
  312.     setpwent();
  313.     pwd = getpwnam(locuser);
  314.     if (pwd == NULL) {
  315.         error("Login incorrect.\n");
  316.         exit(1);
  317.     }
  318.     endpwent();
  319.     if (chdir(pwd->pw_dir) < 0) {
  320.         (void) chdir("/");
  321. #ifdef notdef
  322.         error("No remote directory.\n");
  323.         exit(1);
  324. #endif
  325.     }
  326.  
  327.     if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' &&
  328.         ruserok(hostname, pwd->pw_uid == 0, remuser, locuser) < 0) {
  329.         error("Permission denied.\n");
  330.         exit(1);
  331.     }
  332.  
  333.     if (pwd->pw_uid && !access("/etc/nologin", F_OK)) {
  334.         error("Logins currently disabled.\n");
  335.         exit(1);
  336.     }
  337.     (void) write(2, "\0", 1);
  338.  
  339.     if (port) {
  340.         if (pipe(pv) < 0) {
  341.             error("Can't make pipe.\n");
  342.             exit(1);
  343.         }
  344.         pid = fork();
  345.         if (pid == -1)  {
  346.             error("Try again.\n");
  347.             exit(1);
  348.         }
  349.         if (pv[0] > s)
  350.             nfd = pv[0];
  351.         else
  352.             nfd = s;
  353.         nfd++;
  354.         if (pid) {
  355.             (void) close(0); (void) close(1); (void) close(2);
  356.             (void) close(pv[1]);
  357.             FD_ZERO(&readfrom);
  358.             FD_SET(s, &readfrom);
  359.             FD_SET(pv[0], &readfrom);
  360.             ioctl(pv[0], FIONBIO, (char *)&one);
  361.             /* should set s nbio! */
  362.             do {
  363.                 ready = readfrom;
  364.                 if (select(nfd, &ready, (fd_set *)0,
  365.                     (fd_set *)0, (struct timeval *)0) < 0)
  366.                     break;
  367.                 if (FD_ISSET(s, &ready)) {
  368.                     if (read(s, &sig, 1) <= 0)
  369.                         FD_CLR(s, &readfrom);
  370.                     else
  371.                         killpg(pid, sig);
  372.                 }
  373.                 if (FD_ISSET(pv[0], &ready)) {
  374.                     errno = 0;
  375.                     cc = read(pv[0], buf, sizeof (buf));
  376.                     if (cc <= 0) {
  377.                         shutdown(s, 1+1);
  378.                         FD_CLR(pv[0], &readfrom);
  379.                     } else
  380.                         (void) write(s, buf, cc);
  381.                 }
  382.             } while (FD_ISSET(s, &readfrom) ||
  383.                 FD_ISSET(pv[0], &readfrom));
  384.             exit(0);
  385.         }
  386.         setpgrp(0, getpid());
  387.         (void) close(s); (void) close(pv[0]);
  388.         dup2(pv[1], 2);
  389.     }
  390.     if (*pwd->pw_shell == '\0')
  391.         pwd->pw_shell = "/bin/sh";
  392.     (void) setgid((gid_t)pwd->pw_gid);
  393.     initgroups(pwd->pw_name, pwd->pw_gid);
  394.     (void) setuid((uid_t)pwd->pw_uid);
  395.     environ = envinit;
  396.     strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
  397.     strncat(shell, pwd->pw_shell, sizeof(shell)-7);
  398.     strncat(username, pwd->pw_name, sizeof(username)-6);
  399.     cp = rindex(pwd->pw_shell, '/');
  400.     if (cp)
  401.         cp++;
  402.     else
  403.         cp = pwd->pw_shell;
  404.     execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
  405.     perror(pwd->pw_shell);
  406.     exit(1);
  407. }
  408.  
  409. /*VARARGS1*/
  410. error(fmt, a1, a2, a3)
  411.     char *fmt;
  412.     int a1, a2, a3;
  413. {
  414.     char buf[BUFSIZ];
  415.  
  416.     buf[0] = 1;
  417.     (void) sprintf(buf+1, fmt, a1, a2, a3);
  418.     (void) write(2, buf, strlen(buf));
  419. }
  420.  
  421. getstr(buf, cnt, err)
  422.     char *buf;
  423.     int cnt;
  424.     char *err;
  425. {
  426.     char c;
  427.  
  428.     do {
  429.         if (read(0, &c, 1) != 1)
  430.             exit(1);
  431.         *buf++ = c;
  432.         if (--cnt == 0) {
  433.             error("%s too long\n", err);
  434.             exit(1);
  435.         }
  436.     } while (c != 0);
  437. }
  438.  
  439. /*
  440.  * Check whether host h is in our local domain,
  441.  * as determined by the part of the name following
  442.  * the first '.' in its name and in ours.
  443.  * If either name is unqualified (contains no '.'),
  444.  * assume that the host is local, as it will be
  445.  * interpreted as such.
  446.  */
  447. local_domain(h)
  448.     char *h;
  449. {
  450.     char localhost[MAXHOSTNAMELEN];
  451.     char *p1, *p2 = index(h, '.');
  452.  
  453.     (void) gethostname(localhost, sizeof(localhost));
  454.     p1 = index(localhost, '.');
  455.     if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
  456.         return(1);
  457.     return(0);
  458. }
  459.  
  460. do_access(daemon, host, addr, ruser, luser)
  461. char   *daemon;
  462. char   *host;
  463. char   *addr;
  464. char   *ruser;
  465. char   *luser;
  466. {
  467.     int     allow = hosts_ctl(daemon, strcmp(host, addr) ? host : "unknown",
  468.                   addr, ruser);
  469.     char   *text = allow ? "" : "refused ";
  470.  
  471.     if (strcmp(ruser, luser) == 0)
  472.     syslog(LOG_INFO, "%sconnect from %s@%s",
  473.            text, ruser, host);
  474.     else
  475.     syslog(LOG_INFO, "%sconnect from %s@%s to %s",
  476.            text, ruser, host, luser);
  477.     if (!allow)
  478.     exit(0);
  479. }
  480.